package com.gitee.magic.framework.base.utils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.magic.context.ConverterEditorUtils;
import com.gitee.magic.core.annotations.Column;
import com.gitee.magic.core.annotations.SqlColumn;
import com.gitee.magic.core.annotations.SqlCondition;
import com.gitee.magic.core.annotations.SqlCondition.SqlConditionType;
import com.gitee.magic.core.annotations.Transient;
import com.gitee.magic.core.converter.AbstractConverterEditor;
import com.gitee.magic.core.exception.ApplicationException;
import com.gitee.magic.core.utils.ContextSupportType;
import com.gitee.magic.core.utils.ReflectUtils;
import com.gitee.magic.core.utils.StringUtils;
import com.gitee.magic.core.utils.reflect.TypeReference;
import com.gitee.magic.core.valid.ValidContext;
import com.gitee.magic.core.valid.annotation.NotNull;

/**
 * @author start
 */
public class MapConvert {

	private static final Logger LOGGER = LoggerFactory.getLogger(MapConvert.class);
	
	public static <T>T convert(Class<T> classes,Map<String,Object> map) {
		return convert(classes,map,new TypeReference<T>() {});
	}
	
	/**
	 * Map转Object
	 * @param classes
	 * @param map
	 * @return
	 */
	public static <T>T convert(Class<T> classes,Map<String,Object> map,TypeReference<?> reference) {
		T instance=ReflectUtils.newInstance(classes);
		Class<?> prototype=classes;
		do {
			Field[] fields=prototype.getDeclaredFields();
			for(Field field:fields) {
				if(Modifier.isFinal(field.getModifiers())||Modifier.isStatic(field.getModifiers())) {
					continue;
				}
				if(field.isAnnotationPresent(Transient.class)) {
					continue;
				}
				String fieldName=field.getName();
				PropertyDescriptor pd=null;
				try {
					pd = new PropertyDescriptor(fieldName, prototype);
				} catch (IntrospectionException e) {
					if(LOGGER.isErrorEnabled()) {
						LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法",prototype,field.getType(),fieldName);
					}
					throw new ApplicationException(e);
				}
				if(LOGGER.isDebugEnabled()) {
					LOGGER.error("MapConvert类字段名：{}",fieldName);
				}
				//别名
				if(field.isAnnotationPresent(Column.class)) {
					Column column=field.getAnnotation(Column.class);
					fieldName=column.value();
				}
				Object value=null;
				//判断是否存在当前KeyValue对
				if(map.containsKey(fieldName)) {
					value=map.get(fieldName);
				}
				if(value==null){
					try {
						//获取默认值
						value=pd.getReadMethod().invoke(instance);
					} catch (IllegalAccessException e) {
						throw new ApplicationException(e);
					} catch (IllegalArgumentException e) {
						throw new ApplicationException(e);
					} catch (InvocationTargetException e) {
						throw new ApplicationException(e);
					}
					ValidContext.verify(field.getAnnotation(NotNull.class),value,fieldName);
					if(value==null) {
						//如果默认值也为null则直接跳过
						continue;
					}
				}
				//校验
				ValidContext.verifyField(field,value);
				AbstractConverterEditor<?> convertObj=ConverterEditorUtils.getFieldConverterEditor(field, null, reference.getType());
				convertObj.restore(value);
				Object targetValue=convertObj.getValue();
				try {
					pd.getWriteMethod().invoke(instance,targetValue);
				} catch (IllegalAccessException e) {
					throw new ApplicationException(e);
				} catch (IllegalArgumentException e) {
					throw new ApplicationException(e);
				} catch (InvocationTargetException e) {
					throw new ApplicationException(e);
				}
			}
		}while(!(prototype=prototype.getSuperclass()).equals(Object.class));
		return instance;
	}
	
	/**
	 * 转换参数
	 * @param entity
	 * @return
	 */
	public static <T>Map<String,Object> convert(T entity) {
		Map<String,Object> params=new HashMap<>(100);
		Class<?> prototype=entity.getClass();
		do {
			Field[] fields=prototype.getDeclaredFields();
			for(Field field:fields) {
				if(Modifier.isFinal(field.getModifiers())||Modifier.isStatic(field.getModifiers())) {
					continue;
				}
				if(field.isAnnotationPresent(Transient.class)) {
					continue;
				}
				String fieldName=field.getName();
				PropertyDescriptor pd=null;
				try {
					pd = new PropertyDescriptor(fieldName, prototype);
				} catch (IntrospectionException e) {
					if(LOGGER.isErrorEnabled()) {
						LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法",prototype,field.getType(),fieldName);
					}
					throw new ApplicationException(e);
				}
				if(LOGGER.isDebugEnabled()) {
					LOGGER.error("MapConvert类字段名：{}",fieldName);
				}
				//别名
				if(field.isAnnotationPresent(Column.class)) {
					Column column=field.getAnnotation(Column.class);
					fieldName=column.value();
				}
				Object value=null;
				try {
					//获取默认值
					value=pd.getReadMethod().invoke(entity);
				} catch (IllegalAccessException e) {
					throw new ApplicationException(e);
				} catch (IllegalArgumentException e) {
					throw new ApplicationException(e);
				} catch (InvocationTargetException e) {
					throw new ApplicationException(e);
				}
				Object targetValue=null;
				if(value!=null) {
					if(ContextSupportType.isSupportTypeCheckByBoolean(field.getType())
							|| field.getType().isEnum()) {
						targetValue = String.valueOf(value);
					} else {
						targetValue = value;
					}
				}
				params.put(fieldName, targetValue);
			}
		}while(!(prototype=prototype.getSuperclass()).equals(Object.class));
		return params;
	}
	
	/**
	 * 转换参数
	 * @param entity
	 * @return
	 */
	public static <T>List<ParamValue> convertExt(T entity) {
		List<ParamValue> params=new ArrayList<>(100);
		Class<?> prototype=entity.getClass();
		do {
			Field[] fields=prototype.getDeclaredFields();
			for(Field field:fields) {
				if(Modifier.isFinal(field.getModifiers())||Modifier.isStatic(field.getModifiers())) {
					continue;
				}
				if(field.isAnnotationPresent(Transient.class)) {
					continue;
				}
				String fieldName=field.getName();
				PropertyDescriptor pd=null;
				try {
					pd = new PropertyDescriptor(fieldName, prototype);
				} catch (IntrospectionException e) {
					if(LOGGER.isErrorEnabled()) {
						LOGGER.error("Class[{}],Type:[{}],Field:[{}],未获取到get,set方法",prototype,field.getType(),fieldName);
					}
					throw new ApplicationException(e);
				}
				if(LOGGER.isDebugEnabled()) {
					LOGGER.error("MapConvert类字段名：{}",fieldName);
				}
				//别名
				if(field.isAnnotationPresent(Column.class)) {
					Column column=field.getAnnotation(Column.class);
					fieldName=column.value();
				}
				Object value=null;
				try {
					//获取默认值
					value=pd.getReadMethod().invoke(entity);
				} catch (IllegalAccessException e) {
					throw new ApplicationException(e);
				} catch (IllegalArgumentException e) {
					throw new ApplicationException(e);
				} catch (InvocationTargetException e) {
					throw new ApplicationException(e);
				}
				Object targetValue=null;
				if(value!=null) {
					if(ContextSupportType.isSupportTypeCheckByBoolean(field.getType())
							|| field.getType().isEnum()) {
						targetValue = String.valueOf(value);
					} else {
						targetValue = value;
					}
				}
				ParamValue pv=new ParamValue();
				if(field.isAnnotationPresent(SqlCondition.class)) {
					SqlCondition condition=field.getAnnotation(SqlCondition.class);
					pv.setCondition(condition.value());
				}else {
					pv.setCondition(new SqlConditionType[]{SqlConditionType.EQ});
				}
				if(field.isAnnotationPresent(SqlColumn.class)) {
					SqlColumn sqlColumn=field.getAnnotation(SqlColumn.class);
					pv.setColumnName(sqlColumn.value());
					if(StringUtils.isEmpty(sqlColumn.alias())) {
						pv.setAliasColumnName(sqlColumn.value());
					}else {
						pv.setAliasColumnName(sqlColumn.alias());
					}
				}else {
					pv.setColumnName(fieldName);
					pv.setAliasColumnName(fieldName);
				}
				pv.setValue(targetValue);
				params.add(pv);
			}
		}while(!(prototype=prototype.getSuperclass()).equals(Object.class));
		return params;
	}

	/**
	 * 获取当前实体类的字段映射关系
	 * @param prototype
	 * @return
	 */
	public static Map<String,String> mapping(Class<?> prototype) {
		Map<String,String> mapping=new HashMap<>(100);
		do {
			Field[] fields=prototype.getDeclaredFields();
			for(Field field:fields) {
				if(Modifier.isFinal(field.getModifiers())||Modifier.isStatic(field.getModifiers())) {
					continue;
				}
				if(field.isAnnotationPresent(Transient.class)) {
					continue;
				}
				String fieldName=field.getName();
				//别名
				if(field.isAnnotationPresent(Column.class)) {
					Column alias=field.getAnnotation(Column.class);
					fieldName=alias.value();
				}
				mapping.put(field.getName(), fieldName);
			}
		}while(!(prototype=prototype.getSuperclass()).equals(Object.class));
		return mapping;
	}
	
}
